1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.android.launcher;
18
19 import android.content.Context;
20 import android.content.res.TypedArray;
21 import android.content.res.Resources;
22 import android.graphics.Rect;
23 import android.graphics.RectF;
24 import android.util.AttributeSet;
25 import android.view.ContextMenu;
26 import android.view.MotionEvent;
27 import android.view.View;
28 import android.view.ViewDebug;
29 import android.view.ViewGroup;
30
31 import java.util.ArrayList;
32
33 public class CellLayout extends ViewGroup {
34 private boolean mPortrait;
35
36 private int mCellWidth;
37 private int mCellHeight;
38
39 private int mLongAxisStartPadding;
40 private int mLongAxisEndPadding;
41
42 private int mShortAxisStartPadding;
43 private int mShortAxisEndPadding;
44
45 private int mShortAxisCells;
46 private int mLongAxisCells;
47
48 private int mWidthGap;
49 private int mHeightGap;
50
51 private final Rect mRect = new Rect();
52 private final CellInfo mCellInfo = new CellInfo();
53
54 int[] mCellXY = new int[2];
55
56 boolean[][] mOccupied;
57
58 private RectF mDragRect = new RectF();
59
60 private boolean mDirtyTag;
61
62 public CellLayout(Context context) {
63 this(context, null);
64 }
65
66 public CellLayout(Context context, AttributeSet attrs) {
67 this(context, attrs, 0);
68 }
69
70 public CellLayout(Context context, AttributeSet attrs, int defStyle) {
71 super(context, attrs, defStyle);
72 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
73
74 mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
75 mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
76
77 mLongAxisStartPadding =
78 a.getDimensionPixelSize(R.styleable.CellLayout_longAxisStartPadding, 10);
79 mLongAxisEndPadding =
80 a.getDimensionPixelSize(R.styleable.CellLayout_longAxisEndPadding, 10);
81 mShortAxisStartPadding =
82 a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisStartPadding, 10);
83 mShortAxisEndPadding =
84 a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisEndPadding, 10);
85
86 mShortAxisCells = a.getInt(R.styleable.CellLayout_shortAxisCells, 4);
87 mLongAxisCells = a.getInt(R.styleable.CellLayout_longAxisCells, 4);
88
89 a.recycle();
90
91 setAlwaysDrawnWithCacheEnabled(false);
92
93 if (mOccupied == null) {
94 if (mPortrait) {
95 mOccupied = new boolean[mShortAxisCells][mLongAxisCells];
96 } else {
97 mOccupied = new boolean[mLongAxisCells][mShortAxisCells];
98 }
99 }
100 }
101
102 @Override
103 public void cancelLongPress() {
104 super.cancelLongPress();
105
106
107 final int count = getChildCount();
108 for (int i = 0; i < count; i++) {
109 final View child = getChildAt(i);
110 child.cancelLongPress();
111 }
112 }
113
114 int getCountX() {
115 return mPortrait ? mShortAxisCells : mLongAxisCells;
116 }
117
118 int getCountY() {
119 return mPortrait ? mLongAxisCells : mShortAxisCells;
120 }
121
122 @Override
123 public void addView(View child, int index, ViewGroup.LayoutParams params) {
124
125
126 final LayoutParams cellParams = (LayoutParams) params;
127 cellParams.regenerateId = true;
128
129 super.addView(child, index, params);
130 }
131
132 @Override
133 public void requestChildFocus(View child, View focused) {
134 super.requestChildFocus(child, focused);
135 if (child != null) {
136 Rect r = new Rect();
137 child.getDrawingRect(r);
138 requestRectangleOnScreen(r);
139 }
140 }
141
142 @Override
143 protected void onAttachedToWindow() {
144 super.onAttachedToWindow();
145 mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
146 }
147
148 @Override
149 public boolean onInterceptTouchEvent(MotionEvent ev) {
150 final int action = ev.getAction();
151 final CellInfo cellInfo = mCellInfo;
152
153 if (action == MotionEvent.ACTION_DOWN) {
154 final Rect frame = mRect;
155 final int x = (int) ev.getX() + mScrollX;
156 final int y = (int) ev.getY() + mScrollY;
157 final int count = getChildCount();
158
159 boolean found = false;
160 for (int i = count - 1; i >= 0; i--) {
161 final View child = getChildAt(i);
162
163 if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
164 child.getHitRect(frame);
165 if (frame.contains(x, y)) {
166 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
167 cellInfo.cell = child;
168 cellInfo.cellX = lp.cellX;
169 cellInfo.cellY = lp.cellY;
170 cellInfo.spanX = lp.cellHSpan;
171 cellInfo.spanY = lp.cellVSpan;
172 cellInfo.valid = true;
173 found = true;
174 mDirtyTag = false;
175 break;
176 }
177 }
178 }
179
180 if (!found) {
181 int cellXY[] = mCellXY;
182 pointToCellExact(x, y, cellXY);
183
184 final boolean portrait = mPortrait;
185 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
186 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
187
188 final boolean[][] occupied = mOccupied;
189 findOccupiedCells(xCount, yCount, occupied, null);
190
191 cellInfo.cell = null;
192 cellInfo.cellX = cellXY[0];
193 cellInfo.cellY = cellXY[1];
194 cellInfo.spanX = 1;
195 cellInfo.spanY = 1;
196 cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
197 cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
198
199
200
201
202
203
204 mDirtyTag = true;
205 }
206 setTag(cellInfo);
207 } else if (action == MotionEvent.ACTION_UP) {
208 cellInfo.cell = null;
209 cellInfo.cellX = -1;
210 cellInfo.cellY = -1;
211 cellInfo.spanX = 0;
212 cellInfo.spanY = 0;
213 cellInfo.valid = false;
214 mDirtyTag = false;
215 setTag(cellInfo);
216 }
217
218 return false;
219 }
220
221 @Override
222 public CellInfo getTag() {
223 final CellInfo info = (CellInfo) super.getTag();
224 if (mDirtyTag && info.valid) {
225 final boolean portrait = mPortrait;
226 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
227 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
228
229 final boolean[][] occupied = mOccupied;
230 findOccupiedCells(xCount, yCount, occupied, null);
231
232 findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
233
234 mDirtyTag = false;
235 }
236 return info;
237 }
238
239 private static void findIntersectingVacantCells(CellInfo cellInfo, int x, int y,
240 int xCount, int yCount, boolean[][] occupied) {
241
242 cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
243 cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
244 cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
245 cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
246 cellInfo.clearVacantCells();
247
248 if (occupied[x][y]) {
249 return;
250 }
251
252 cellInfo.current.set(x, y, x, y);
253
254 findVacantCell(cellInfo.current, xCount, yCount, occupied, cellInfo);
255 }
256
257 private static void findVacantCell(Rect current, int xCount, int yCount, boolean[][] occupied,
258 CellInfo cellInfo) {
259
260 addVacantCell(current, cellInfo);
261
262 if (current.left > 0) {
263 if (isColumnEmpty(current.left - 1, current.top, current.bottom, occupied)) {
264 current.left--;
265 findVacantCell(current, xCount, yCount, occupied, cellInfo);
266 current.left++;
267 }
268 }
269
270 if (current.right < xCount - 1) {
271 if (isColumnEmpty(current.right + 1, current.top, current.bottom, occupied)) {
272 current.right++;
273 findVacantCell(current, xCount, yCount, occupied, cellInfo);
274 current.right--;
275 }
276 }
277
278 if (current.top > 0) {
279 if (isRowEmpty(current.top - 1, current.left, current.right, occupied)) {
280 current.top--;
281 findVacantCell(current, xCount, yCount, occupied, cellInfo);
282 current.top++;
283 }
284 }
285
286 if (current.bottom < yCount - 1) {
287 if (isRowEmpty(current.bottom + 1, current.left, current.right, occupied)) {
288 current.bottom++;
289 findVacantCell(current, xCount, yCount, occupied, cellInfo);
290 current.bottom--;
291 }
292 }
293 }
294
295 private static void addVacantCell(Rect current, CellInfo cellInfo) {
296 CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
297 cell.cellX = current.left;
298 cell.cellY = current.top;
299 cell.spanX = current.right - current.left + 1;
300 cell.spanY = current.bottom - current.top + 1;
301 if (cell.spanX > cellInfo.maxVacantSpanX) {
302 cellInfo.maxVacantSpanX = cell.spanX;
303 cellInfo.maxVacantSpanXSpanY = cell.spanY;
304 }
305 if (cell.spanY > cellInfo.maxVacantSpanY) {
306 cellInfo.maxVacantSpanY = cell.spanY;
307 cellInfo.maxVacantSpanYSpanX = cell.spanX;
308 }
309 cellInfo.vacantCells.add(cell);
310 }
311
312 private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) {
313 for (int y = top; y <= bottom; y++) {
314 if (occupied[x][y]) {
315 return false;
316 }
317 }
318 return true;
319 }
320
321 private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
322 for (int x = left; x <= right; x++) {
323 if (occupied[x][y]) {
324 return false;
325 }
326 }
327 return true;
328 }
329
330 CellInfo findAllVacantCells(boolean[] occupiedCells, View ignoreView) {
331 final boolean portrait = mPortrait;
332 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
333 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
334
335 boolean[][] occupied = mOccupied;
336
337 if (occupiedCells != null) {
338 for (int y = 0; y < yCount; y++) {
339 for (int x = 0; x < xCount; x++) {
340 occupied[x][y] = occupiedCells[y * xCount + x];
341 }
342 }
343 } else {
344 findOccupiedCells(xCount, yCount, occupied, ignoreView);
345 }
346
347 return findAllVacantCellsFromOccupied(occupied, xCount, yCount);
348 }
349
350
351
352
353
354 CellInfo findAllVacantCellsFromOccupied(boolean[][] occupied,
355 final int xCount, final int yCount) {
356 CellInfo cellInfo = new CellInfo();
357
358 cellInfo.cellX = -1;
359 cellInfo.cellY = -1;
360 cellInfo.spanY = 0;
361 cellInfo.spanX = 0;
362 cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
363 cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
364 cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
365 cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
366 cellInfo.screen = mCellInfo.screen;
367
368 Rect current = cellInfo.current;
369
370 for (int x = 0; x < xCount; x++) {
371 for (int y = 0; y < yCount; y++) {
372 if (!occupied[x][y]) {
373 current.set(x, y, x, y);
374 findVacantCell(current, xCount, yCount, occupied, cellInfo);
375 occupied[x][y] = true;
376 }
377 }
378 }
379
380 cellInfo.valid = cellInfo.vacantCells.size() > 0;
381
382
383
384
385 return cellInfo;
386 }
387
388
389
390
391
392
393
394 void pointToCellExact(int x, int y, int[] result) {
395 final boolean portrait = mPortrait;
396
397 final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
398 final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
399
400 result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
401 result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
402
403 final int xAxis = portrait ? mShortAxisCells : mLongAxisCells;
404 final int yAxis = portrait ? mLongAxisCells : mShortAxisCells;
405
406 if (result[0] < 0) result[0] = 0;
407 if (result[0] >= xAxis) result[0] = xAxis - 1;
408 if (result[1] < 0) result[1] = 0;
409 if (result[1] >= yAxis) result[1] = yAxis - 1;
410 }
411
412
413
414
415
416
417
418
419
420 void cellToPoint(int cellX, int cellY, int[] result) {
421 final boolean portrait = mPortrait;
422
423 final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
424 final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
425
426
427 result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
428 result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
429 }
430
431 @Override
432 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
433
434
435 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
436 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
437
438 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
439 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
440
441 if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
442 throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
443 }
444
445 final int shortAxisCells = mShortAxisCells;
446 final int longAxisCells = mLongAxisCells;
447 final int longAxisStartPadding = mLongAxisStartPadding;
448 final int longAxisEndPadding = mLongAxisEndPadding;
449 final int shortAxisStartPadding = mShortAxisStartPadding;
450 final int shortAxisEndPadding = mShortAxisEndPadding;
451 final int cellWidth = mCellWidth;
452 final int cellHeight = mCellHeight;
453
454 mPortrait = heightSpecSize > widthSpecSize;
455
456 int numShortGaps = shortAxisCells - 1;
457 int numLongGaps = longAxisCells - 1;
458
459 if (mPortrait) {
460 int vSpaceLeft = heightSpecSize - longAxisStartPadding - longAxisEndPadding
461 - (cellHeight * longAxisCells);
462 mHeightGap = vSpaceLeft / numLongGaps;
463
464 int hSpaceLeft = widthSpecSize - shortAxisStartPadding - shortAxisEndPadding
465 - (cellWidth * shortAxisCells);
466 if (numShortGaps > 0) {
467 mWidthGap = hSpaceLeft / numShortGaps;
468 } else {
469 mWidthGap = 0;
470 }
471 } else {
472 int hSpaceLeft = widthSpecSize - longAxisStartPadding - longAxisEndPadding
473 - (cellWidth * longAxisCells);
474 mWidthGap = hSpaceLeft / numLongGaps;
475
476 int vSpaceLeft = heightSpecSize - shortAxisStartPadding - shortAxisEndPadding
477 - (cellHeight * shortAxisCells);
478 if (numShortGaps > 0) {
479 mHeightGap = vSpaceLeft / numShortGaps;
480 } else {
481 mHeightGap = 0;
482 }
483 }
484
485 int count = getChildCount();
486
487 for (int i = 0; i < count; i++) {
488 View child = getChildAt(i);
489 LayoutParams lp = (LayoutParams) child.getLayoutParams();
490
491 if (mPortrait) {
492 lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, shortAxisStartPadding,
493 longAxisStartPadding);
494 } else {
495 lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, longAxisStartPadding,
496 shortAxisStartPadding);
497 }
498
499 if (lp.regenerateId) {
500 child.setId(((getId() & 0xFF) << 16) | (lp.cellX & 0xFF) << 8 | (lp.cellY & 0xFF));
501 lp.regenerateId = false;
502 }
503
504 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
505 int childheightMeasureSpec =
506 MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
507 child.measure(childWidthMeasureSpec, childheightMeasureSpec);
508 }
509
510 setMeasuredDimension(widthSpecSize, heightSpecSize);
511 }
512
513 @Override
514 protected void onLayout(boolean changed, int l, int t, int r, int b) {
515 int count = getChildCount();
516
517 for (int i = 0; i < count; i++) {
518 View child = getChildAt(i);
519 if (child.getVisibility() != GONE) {
520
521 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
522
523 int childLeft = lp.x;
524 int childTop = lp.y;
525 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
526 }
527 }
528 }
529
530 @Override
531 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
532 final int count = getChildCount();
533 for (int i = 0; i < count; i++) {
534 final View view = getChildAt(i);
535 view.setDrawingCacheEnabled(enabled);
536
537 view.buildDrawingCache(true);
538 }
539 }
540
541 @Override
542 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
543 super.setChildrenDrawnWithCacheEnabled(enabled);
544 }
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559 int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
560 CellInfo vacantCells, int[] recycle) {
561
562
563 final int[] bestXY = recycle != null ? recycle : new int[2];
564 final int[] cellXY = mCellXY;
565 double bestDistance = Double.MAX_VALUE;
566
567
568 if (!vacantCells.valid) {
569 return null;
570 }
571
572
573 final int size = vacantCells.vacantCells.size();
574 for (int i = 0; i < size; i++) {
575 final CellInfo.VacantCell cell = vacantCells.vacantCells.get(i);
576
577
578 if (cell.spanX != spanX || cell.spanY != spanY) {
579 continue;
580 }
581
582
583 cellToPoint(cell.cellX, cell.cellY, cellXY);
584
585 double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2) +
586 Math.pow(cellXY[1] - pixelY, 2));
587 if (distance <= bestDistance) {
588 bestDistance = distance;
589 bestXY[0] = cell.cellX;
590 bestXY[1] = cell.cellY;
591 }
592 }
593
594
595 if (bestDistance < Double.MAX_VALUE) {
596 return bestXY;
597 } else {
598 return null;
599 }
600 }
601
602
603
604
605
606
607
608 void onDropChild(View child, int[] targetXY) {
609 if (child != null) {
610 LayoutParams lp = (LayoutParams) child.getLayoutParams();
611 lp.cellX = targetXY[0];
612 lp.cellY = targetXY[1];
613 lp.isDragging = false;
614 mDragRect.setEmpty();
615 child.requestLayout();
616 invalidate();
617 }
618 }
619
620 void onDropAborted(View child) {
621 if (child != null) {
622 ((LayoutParams) child.getLayoutParams()).isDragging = false;
623 invalidate();
624 }
625 mDragRect.setEmpty();
626 }
627
628
629
630
631
632
633 void onDragChild(View child) {
634 LayoutParams lp = (LayoutParams) child.getLayoutParams();
635 lp.isDragging = true;
636 mDragRect.setEmpty();
637 }
638
639
640
641
642
643
644
645
646 public int[] rectToCell(int width, int height) {
647
648
649 final Resources resources = getResources();
650 int actualWidth = resources.getDimensionPixelSize(R.dimen.cell_width);
651 int actualHeight = resources.getDimensionPixelSize(R.dimen.cell_height);
652 int smallerSize = Math.min(actualWidth, actualHeight);
653
654
655 int spanX = (width + smallerSize) / smallerSize;
656 int spanY = (height + smallerSize) / smallerSize;
657
658 return new int[] { spanX, spanY };
659 }
660
661
662
663
664
665
666
667
668
669
670 public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
671 final boolean portrait = mPortrait;
672 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
673 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
674 final boolean[][] occupied = mOccupied;
675
676 findOccupiedCells(xCount, yCount, occupied, null);
677
678 return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
679 }
680
681 static boolean findVacantCell(int[] vacant, int spanX, int spanY,
682 int xCount, int yCount, boolean[][] occupied) {
683
684 for (int x = 0; x < xCount; x++) {
685 for (int y = 0; y < yCount; y++) {
686 boolean available = !occupied[x][y];
687 out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
688 for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
689 available = available && !occupied[i][j];
690 if (!available) break out;
691 }
692 }
693
694 if (available) {
695 vacant[0] = x;
696 vacant[1] = y;
697 return true;
698 }
699 }
700 }
701
702 return false;
703 }
704
705 boolean[] getOccupiedCells() {
706 final boolean portrait = mPortrait;
707 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
708 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
709 final boolean[][] occupied = mOccupied;
710
711 findOccupiedCells(xCount, yCount, occupied, null);
712
713 final boolean[] flat = new boolean[xCount * yCount];
714 for (int y = 0; y < yCount; y++) {
715 for (int x = 0; x < xCount; x++) {
716 flat[y * xCount + x] = occupied[x][y];
717 }
718 }
719
720 return flat;
721 }
722
723 private void findOccupiedCells(int xCount, int yCount, boolean[][] occupied, View ignoreView) {
724 for (int x = 0; x < xCount; x++) {
725 for (int y = 0; y < yCount; y++) {
726 occupied[x][y] = false;
727 }
728 }
729
730 int count = getChildCount();
731 for (int i = 0; i < count; i++) {
732 View child = getChildAt(i);
733 if (child instanceof Folder || child.equals(ignoreView)) {
734 continue;
735 }
736 LayoutParams lp = (LayoutParams) child.getLayoutParams();
737
738 for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) {
739 for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) {
740 occupied[x][y] = true;
741 }
742 }
743 }
744 }
745
746 @Override
747 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
748 return new CellLayout.LayoutParams(getContext(), attrs);
749 }
750
751 @Override
752 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
753 return p instanceof CellLayout.LayoutParams;
754 }
755
756 @Override
757 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
758 return new CellLayout.LayoutParams(p);
759 }
760
761 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
762
763
764
765 @ViewDebug.ExportedProperty
766 public int cellX;
767
768
769
770
771 @ViewDebug.ExportedProperty
772 public int cellY;
773
774
775
776
777 @ViewDebug.ExportedProperty
778 public int cellHSpan;
779
780
781
782
783 @ViewDebug.ExportedProperty
784 public int cellVSpan;
785
786
787
788
789 public boolean isDragging;
790
791
792 @ViewDebug.ExportedProperty
793 int x;
794
795 @ViewDebug.ExportedProperty
796 int y;
797
798 boolean regenerateId;
799
800 public LayoutParams(Context c, AttributeSet attrs) {
801 super(c, attrs);
802 cellHSpan = 1;
803 cellVSpan = 1;
804 }
805
806 public LayoutParams(ViewGroup.LayoutParams source) {
807 super(source);
808 cellHSpan = 1;
809 cellVSpan = 1;
810 }
811
812 public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
813 super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
814 this.cellX = cellX;
815 this.cellY = cellY;
816 this.cellHSpan = cellHSpan;
817 this.cellVSpan = cellVSpan;
818 }
819
820 public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
821 int hStartPadding, int vStartPadding) {
822
823 final int myCellHSpan = cellHSpan;
824 final int myCellVSpan = cellVSpan;
825 final int myCellX = cellX;
826 final int myCellY = cellY;
827
828 width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
829 leftMargin - rightMargin;
830 height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
831 topMargin - bottomMargin;
832
833 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
834 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
835 }
836 }
837
838 static final class CellInfo implements ContextMenu.ContextMenuInfo {
839
840
841
842
843
844
845 static final class VacantCell {
846 int cellX;
847 int cellY;
848 int spanX;
849 int spanY;
850
851
852
853
854
855 private static final int POOL_LIMIT = 100;
856 private static final Object sLock = new Object();
857
858 private static int sAcquiredCount = 0;
859 private static VacantCell sRoot;
860
861 private VacantCell next;
862
863 static VacantCell acquire() {
864 synchronized (sLock) {
865 if (sRoot == null) {
866 return new VacantCell();
867 }
868
869 VacantCell info = sRoot;
870 sRoot = info.next;
871 sAcquiredCount--;
872
873 return info;
874 }
875 }
876
877 void release() {
878 synchronized (sLock) {
879 if (sAcquiredCount < POOL_LIMIT) {
880 sAcquiredCount++;
881 next = sRoot;
882 sRoot = this;
883 }
884 }
885 }
886
887 @Override
888 public String toString() {
889 return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX=" + spanX +
890 ", spanY=" + spanY + "]";
891 }
892 }
893
894 View cell;
895 int cellX;
896 int cellY;
897 int spanX;
898 int spanY;
899 int screen;
900 boolean valid;
901
902 final ArrayList<VacantCell> vacantCells = new ArrayList<VacantCell>(VacantCell.POOL_LIMIT);
903 int maxVacantSpanX;
904 int maxVacantSpanXSpanY;
905 int maxVacantSpanY;
906 int maxVacantSpanYSpanX;
907 final Rect current = new Rect();
908
909 void clearVacantCells() {
910 final ArrayList<VacantCell> list = vacantCells;
911 final int count = list.size();
912
913 for (int i = 0; i < count; i++) list.get(i).release();
914
915 list.clear();
916 }
917
918 void findVacantCellsFromOccupied(boolean[] occupied, int xCount, int yCount) {
919 if (cellX < 0 || cellY < 0) {
920 maxVacantSpanX = maxVacantSpanXSpanY = Integer.MIN_VALUE;
921 maxVacantSpanY = maxVacantSpanYSpanX = Integer.MIN_VALUE;
922 clearVacantCells();
923 return;
924 }
925
926 final boolean[][] unflattened = new boolean[xCount][yCount];
927 for (int y = 0; y < yCount; y++) {
928 for (int x = 0; x < xCount; x++) {
929 unflattened[x][y] = occupied[y * xCount + x];
930 }
931 }
932 CellLayout.findIntersectingVacantCells(this, cellX, cellY, xCount, yCount, unflattened);
933 }
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949 boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
950 return findCellForSpan(cellXY, spanX, spanY, true);
951 }
952
953 boolean findCellForSpan(int[] cellXY, int spanX, int spanY, boolean clear) {
954 final ArrayList<VacantCell> list = vacantCells;
955 final int count = list.size();
956
957 boolean found = false;
958
959 if (this.spanX >= spanX && this.spanY >= spanY) {
960 cellXY[0] = cellX;
961 cellXY[1] = cellY;
962 found = true;
963 }
964
965
966 for (int i = 0; i < count; i++) {
967 VacantCell cell = list.get(i);
968 if (cell.spanX == spanX && cell.spanY == spanY) {
969 cellXY[0] = cell.cellX;
970 cellXY[1] = cell.cellY;
971 found = true;
972 break;
973 }
974 }
975
976
977 for (int i = 0; i < count; i++) {
978 VacantCell cell = list.get(i);
979 if (cell.spanX >= spanX && cell.spanY >= spanY) {
980 cellXY[0] = cell.cellX;
981 cellXY[1] = cell.cellY;
982 found = true;
983 break;
984 }
985 }
986
987 if (clear) clearVacantCells();
988
989 return found;
990 }
991
992 @Override
993 public String toString() {
994 return "Cell[view=" + (cell == null ? "null" : cell.getClass()) + ", x=" + cellX +
995 ", y=" + cellY + "]";
996 }
997 }
998 }
999
1000